feat: Add deeplinks for recording controls + Raycast extension#1632
feat: Add deeplinks for recording controls + Raycast extension#1632DebuggingMax wants to merge 1 commit intoCapSoftware:mainfrom
Conversation
Adds new deeplink actions for recording control: - pause_recording - Pause the current recording - resume_recording - Resume a paused recording - toggle_pause_recording - Toggle pause/resume state - restart_recording - Restart the current recording - set_microphone - Switch microphone input - set_camera - Switch camera input Also includes a complete Raycast extension with commands for: - Start Recording - Stop Recording - Pause Recording - Resume Recording - Toggle Pause - Restart Recording - Open Settings Closes CapSoftware#1540
| DeepLinkAction::SetMicrophone { label } => { | ||
| let state = app.state::<ArcLock<App>>(); | ||
| crate::set_mic_input(state, label).await | ||
| } | ||
| DeepLinkAction::SetCamera { id } => { | ||
| let state = app.state::<ArcLock<App>>(); | ||
| crate::set_camera_input(app.clone(), state, id, None).await | ||
| } |
There was a problem hiding this comment.
set_mic_input and set_camera_input are not pub in lib.rs, but are called here with crate:: prefix. This will fail compilation.
| DeepLinkAction::SetMicrophone { label } => { | |
| let state = app.state::<ArcLock<App>>(); | |
| crate::set_mic_input(state, label).await | |
| } | |
| DeepLinkAction::SetCamera { id } => { | |
| let state = app.state::<ArcLock<App>>(); | |
| crate::set_camera_input(app.clone(), state, id, None).await | |
| } | |
| DeepLinkAction::SetMicrophone { label } => { | |
| let state = app.state::<ArcLock<App>>(); | |
| crate::commands::set_mic_input(state, label).await | |
| } | |
| DeepLinkAction::SetCamera { id } => { | |
| let state = app.state::<ArcLock<App>>(); | |
| crate::commands::set_camera_input(app.clone(), state, id, None).await | |
| } |
Or make set_mic_input and set_camera_input public in lib.rs by adding pub before async fn.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 173-180
Comment:
`set_mic_input` and `set_camera_input` are not `pub` in `lib.rs`, but are called here with `crate::` prefix. This will fail compilation.
```suggestion
DeepLinkAction::SetMicrophone { label } => {
let state = app.state::<ArcLock<App>>();
crate::commands::set_mic_input(state, label).await
}
DeepLinkAction::SetCamera { id } => {
let state = app.state::<ArcLock<App>>();
crate::commands::set_camera_input(app.clone(), state, id, None).await
}
```
Or make `set_mic_input` and `set_camera_input` public in `lib.rs` by adding `pub` before `async fn`.
How can I resolve this? If you propose a fix, please make it concise.| type DeepLinkAction = | ||
| | { stop_recording: Record<string, never> } | ||
| | { pause_recording: Record<string, never> } | ||
| | { resume_recording: Record<string, never> } | ||
| | { toggle_pause_recording: Record<string, never> } | ||
| | { restart_recording: Record<string, never> } | ||
| | { set_microphone: { label: string | null } } | ||
| | { set_camera: { id: string | null } } | ||
| | { open_settings: { page: string | null } }; |
There was a problem hiding this comment.
Missing start_recording variant in the DeepLinkAction type definition. While start-recording.ts defines its own interface, this type should include all possible actions for consistency and type safety.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/utils/deeplink.ts
Line: 5-13
Comment:
Missing `start_recording` variant in the `DeepLinkAction` type definition. While `start-recording.ts` defines its own interface, this type should include all possible actions for consistency and type safety.
How can I resolve this? If you propose a fix, please make it concise.| ## Requirements | ||
|
|
||
| - [Cap](https://cap.so) must be installed and running | ||
| - macOS only (Cap deeplinks use the `cap-desktop://` scheme) |
There was a problem hiding this comment.
DEEPLINKS.md states that Cap deeplinks work on both macOS and Windows, but this README says "macOS only". Update to clarify platform support or remove if Windows is supported.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/README.md
Line: 18
Comment:
DEEPLINKS.md states that Cap deeplinks work on both macOS and Windows, but this README says "macOS only". Update to clarify platform support or remove if Windows is supported.
How can I resolve this? If you propose a fix, please make it concise.| export default async function Command() { | ||
| const action: StartRecordingAction = { | ||
| start_recording: { | ||
| capture_mode: { screen: "Main Display" }, |
There was a problem hiding this comment.
Hardcoded "Main Display" may not match the actual display name on all systems. If the display name doesn't match, the deeplink will fail with "No screen with name" error (see deeplink_actions.rs:137). Consider adding configuration or detecting the primary display name.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/start-recording.ts
Line: 18
Comment:
Hardcoded "Main Display" may not match the actual display name on all systems. If the display name doesn't match, the deeplink will fail with "No screen with name" error (see `deeplink_actions.rs:137`). Consider adding configuration or detecting the primary display name.
How can I resolve this? If you propose a fix, please make it concise.| const DEEPLINK_SCHEME = "cap-desktop://action"; | ||
|
|
||
| type DeepLinkAction = | ||
| | { stop_recording: Record<string, never> } |
There was a problem hiding this comment.
I think there’s a serialization mismatch here: DeepLinkAction in Rust has unit variants for stop/pause/resume/toggle/restart, so serde_json expects those as JSON strings (e.g. "stop_recording"). This extension is currently sending objects with empty payloads (e.g. { "stop_recording": {} }), which likely won’t deserialize and will make these commands no-op.
Options:
- Raycast side: send a JSON string for unit actions (keep object form for
start_recording/set_*/open_settings). - Rust side: change unit variants to empty struct variants so
{ "stop_recording": {} }works consistently.
| export default async function Command() { | ||
| const action: StartRecordingAction = { | ||
| start_recording: { | ||
| capture_mode: { screen: "Main Display" }, |
There was a problem hiding this comment.
Hardcoding capture_mode: { screen: "Main Display" } seems brittle since the desktop handler matches on the exact display/window name it enumerates. Consider making the target configurable via Raycast preferences (or prompting), otherwise this will fail for users whose primary display name doesn’t match this string.
|
|
||
| ## How It Works | ||
|
|
||
| This extension uses Cap's deeplink API to control recordings. Each command sends a URL like: |
There was a problem hiding this comment.
The docs here show raw JSON embedded in the URL (and as an object). In practice value needs to be URL-encoded JSON, and unit actions likely need to be a JSON string to match the Rust enum.
| This extension uses Cap's deeplink API to control recordings. Each command sends a URL like: | |
| This extension uses Cap's deeplink API to control recordings. Each command sends a URL like (the `value` param must be URL-encoded JSON): | |
cap-desktop://action?value=%22stop_recording%22
|
|
||
| ### Recording Controls | ||
|
|
||
| #### Stop Recording |
There was a problem hiding this comment.
If the desktop enum keeps StopRecording/PauseRecording/etc as unit variants, these examples should be JSON strings (not { "stop_recording": {} }). Also the URL examples should encode the quotes.
| #### Stop Recording | |
| #### Stop Recording | |
| ```json | |
| "stop_recording" |
Pause Recording
"pause_recording"Resume Recording
"resume_recording"Toggle Pause/Resume
"toggle_pause_recording"Restart Recording
Stops and immediately restarts with the same settings:
"restart_recording"|
|
||
| This extension uses Cap's deeplink API to control recordings. Each command sends a URL like: | ||
|
|
||
| ``` |
There was a problem hiding this comment.
Formatting nit: GitHub suggestions don’t handle nested triple-backtick fences well. Here’s the same change without fenced-code nesting.
| ``` | |
| This extension uses Cap's deeplink API to control recordings. Each command sends a URL like (the `value` param must be URL-encoded JSON): | |
| cap-desktop://action?value=%22stop_recording%22 |
| ### Recording Controls | ||
|
|
||
| #### Stop Recording | ||
| ```json |
There was a problem hiding this comment.
Same note about suggestion formatting: avoiding backtick fences makes this apply cleanly.
| ```json | |
| #### Stop Recording | |
| ~~~json | |
| "stop_recording" | |
| ~~~ | |
| #### Pause Recording | |
| ~~~json | |
| "pause_recording" | |
| ~~~ | |
| #### Resume Recording | |
| ~~~json | |
| "resume_recording" | |
| ~~~ | |
| #### Toggle Pause/Resume | |
| ~~~json | |
| "toggle_pause_recording" | |
| ~~~ | |
| #### Restart Recording | |
| Stops and immediately restarts with the same settings: | |
| ~~~json | |
| "restart_recording" | |
| ~~~ |
Summary
This PR implements deeplinks support for recording controls and a complete Raycast extension, as requested in #1540.
New Deeplink Actions
The following deeplink actions have been added to
deeplink_actions.rs:pause_recordingresume_recordingtoggle_pause_recordingrestart_recordingset_microphoneset_cameraDeeplink URL Format
Examples:
cap-desktop://action?value=%7B%22stop_recording%22%3A%7B%7D%7Dcap-desktop://action?value=%7B%22toggle_pause_recording%22%3A%7B%7D%7DRaycast Extension
Located in
extensions/raycast/, includes:Documentation
Full deeplink documentation is included in
extensions/raycast/DEEPLINKS.mdwith examples for:Testing
The Raycast extension can be tested by:
cd extensions/raycast && npm install && npm run devCloses #1540
Bounty: $200
Greptile Summary
Added deeplink support for recording controls (pause, resume, toggle, restart, set microphone/camera) and a complete Raycast extension with 7 commands. The implementation extends the existing deeplink system in
deeplink_actions.rsto support new recording control actions, and provides a TypeScript-based Raycast extension that constructs and triggers these deeplinks.Key Changes:
DeepLinkActionenum with 6 new variants for recording controls and device configurationCritical Issues:
set_mic_inputandset_camera_inputfunctions are not public but called withcrate::prefix, causing build failureMinor Issues:
start_recordingvariant for consistencyConfidence Score: 1/5
deeplink_actions.rscallscrate::set_mic_inputandcrate::set_camera_input, but these functions are not public inlib.rs. This will cause build failure. Additionally, the hardcoded "Main Display" in start recording will cause runtime failures on systems with different display names. The Raycast extension code itself is well-structured, but the underlying Rust integration has blocking issues.apps/desktop/src-tauri/src/deeplink_actions.rsrequires immediate attention for compilation errors, andextensions/raycast/src/start-recording.tsneeds fixes for cross-system compatibilityImportant Files Changed
Sequence Diagram
sequenceDiagram participant User participant Raycast participant DeeplinkHandler as Cap Deeplink Handler participant DeeplinkActions as deeplink_actions.rs participant Recording as recording.rs participant App as Cap App State User->>Raycast: Trigger command (e.g., "Stop Recording") Raycast->>Raycast: Build JSON action payload Raycast->>Raycast: URL encode payload Raycast->>DeeplinkHandler: Open cap-desktop://action?value={...} DeeplinkHandler->>DeeplinkActions: Parse URL and deserialize JSON alt Stop Recording DeeplinkActions->>Recording: stop_recording(app, state) Recording->>App: Update recording state Recording-->>DeeplinkActions: Result else Pause/Resume/Toggle DeeplinkActions->>Recording: pause/resume/toggle_pause_recording() Recording->>App: Update pause state Recording-->>DeeplinkActions: Result else Set Microphone/Camera DeeplinkActions->>App: set_mic_input/set_camera_input() App->>App: Update device configuration App-->>DeeplinkActions: Result end DeeplinkActions-->>DeeplinkHandler: Success/Error DeeplinkHandler-->>Raycast: Execution result Raycast->>User: Show HUD notificationLast reviewed commit: f03b16d
(2/5) Greptile learns from your feedback when you react with thumbs up/down!